package org.dtrd.modules.record.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.dtrd.common.util.CheckUtil;
import org.dtrd.common.util.DateUtils;
import org.dtrd.config.system.Constant;
import org.dtrd.config.system.Constant.BloodPressureRecordType;
import org.dtrd.config.system.Constant.RecordType;
import org.dtrd.modules.achievement.service.IDtrdEntRdSigninRecordService;
import org.dtrd.modules.device.entity.bean.bioland.SrcDataValue;
import org.dtrd.modules.patient.entity.bean.PatientInfo;
import org.dtrd.modules.patient.service.IDtrdEntRdPatientService;
import org.dtrd.modules.record.entity.bean.*;
import org.dtrd.modules.record.entity.po.DtrdEntRdSigninBloodPressure;
import org.dtrd.modules.record.mapper.DtrdEntRdBloodPressureMapper;
import org.dtrd.modules.record.service.IBloodPressureRecordService;
import org.dtrd.modules.util.MathUtil;
import org.dtrd.common.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.ParseException;
import java.util.*;

/**
 * @author 萧
 * @date:2021-08-22
 **/
@Service
@DS("multi-datasource1")
@Slf4j
public class BloodPressureRecordServiceImpl extends ServiceImpl<DtrdEntRdBloodPressureMapper, DtrdEntRdSigninBloodPressure> implements IBloodPressureRecordService {

    @Autowired
    private IDtrdEntRdPatientService patientService;
    @Autowired
    private IDtrdEntRdSigninRecordService recordService;

    @Override
    public DtrdEntRdSigninBloodPressure transformRecord(CheckinRecord record) {
        DtrdEntRdSigninBloodPressure dt = new DtrdEntRdSigninBloodPressure().init();
        dt.buildWithParams(record.getHighPressure(), record.getLowPressure(), record.getHeartPulse());
        dt.setPatientId(record.getPatientId());
        dt.setBloodPressureType(record.getSubType());
        dt.setRecordTime(parseStringAsDateTime(record.getRecordTime()));
        dt.setRecordDate(parseStringAsDate(record.getRecordTime()));
        dt.setBloodPressureRemark(record.getRemark());
        dt.setRecordPhoto(record.getImages());
        return dt;
    }

    @Override
    public CheckinRecord transformPO(DtrdEntRdSigninBloodPressure po) {
        if (po == null) {
            return null;
        }
        CheckinRecord record = new CheckinRecord();
        record.setRecordTime(DateUtils.formatTime(po.getRecordTime()));
        record.setRecordTimeHm(DateUtils.formatShortTime(po.getRecordTime()));
        record.setDataId(po.getDataId());
        record.setHighPressure(po.getBloodPressureHighPressure());
        record.setLowPressure(po.getBloodPressureLowVoltage());
        record.setHeartPulse(po.getBloodPressureHeartRate());
        record.setImages(po.getRecordPhoto());
        record.setPatientId(po.getPatientId());
        return record;
    }

    @Override
    public LambdaQueryWrapper<DtrdEntRdSigninBloodPressure> generateSelectRecordByDateQuery(Integer patientId, String date, Integer subType) {
        LambdaQueryWrapper<DtrdEntRdSigninBloodPressure> query = new LambdaQueryWrapper<>();
        query.eq(DtrdEntRdSigninBloodPressure::getIsDel, 0)
                .eq(DtrdEntRdSigninBloodPressure::getPatientId, patientId)
                .eq(DtrdEntRdSigninBloodPressure::getRecordDate, date).eq(DtrdEntRdSigninBloodPressure::getBloodPressureType, subType);
        return query;
    }

    @Override
    public void saveBiolandRecord(SrcDataValue srcDatavalue, Integer userId) {
        DtrdEntRdSigninBloodPressure bloodPressureVO = new DtrdEntRdSigninBloodPressure().init();
        bloodPressureVO.setPatientId(userId);
        String checkTime = srcDatavalue.getCheckTime();
        bloodPressureVO.setRecordTime(DateUtils.str2Date(checkTime, DateUtils.datetimeFormat.get()));
        bloodPressureVO.setRecordDate(DateUtils.str2Date(checkTime, DateUtils.date_sdf.get()));
        bloodPressureVO.setBloodPressureHighPressure(parseToInt(srcDatavalue.getHighBloodPressure()));
        bloodPressureVO.setBloodPressureLowVoltage(parseToInt(srcDatavalue.getLowBloodPressure()));
        bloodPressureVO.setBloodPressureHeartRate(srcDatavalue.getPulse());
        save(bloodPressureVO);
    }

    @Override
    public PeriodBloodPressure getBloodPressureRecordsForCharts(Integer patientId, String startDate, String endDate) {
        List<DtrdEntRdSigninBloodPressure> list = baseMapper.getBloodPressureRecordsForCharts(patientId, startDate, endDate);
        PeriodBloodPressure record = new PeriodBloodPressure();
        List<String> dates = new ArrayList<>(list.size());
        List<Integer> values1 = new ArrayList<>(list.size());
        List<Integer> values2 = new ArrayList<>(list.size());
        List<Integer> values3 = new ArrayList<>(list.size());
        if (CheckUtil.isNotEmpty(list)) {
            list.forEach(po -> {
                dates.add(DateUtils.formatDate(po.getRecordDate()));
                values1.add(po.getBloodPressureHighPressure());
                values2.add(po.getBloodPressureLowVoltage());
                values3.add(po.getBloodPressureHeartRate());
            });
        }
        record.setDates(dates);
        record.setValues1(values1);
        record.setValues2(values2);
        List<List<Integer>> values = new ArrayList<>();
        values.add(values1);
        values.add(values2);
        values.add(values3);
        record.setValue(values);
        return record;
    }

    @Override
    public List<BloodBean> getAllBloodPressureByPage(int patientId, int page, int pageSize) {
        int pageStart = (page - 1) * pageSize;
        List<DtrdEntRdSigninBloodPressure> list = baseMapper.getAllBloodPressureByPage(patientId, pageStart, pageSize);
        return transformPoList(list);
    }

    @Override
    public List<BloodBean> getAllBloodPressureByDate(int patientId, String startDate, String endDate, boolean isAsc) {
        List<DtrdEntRdSigninBloodPressure> list = baseMapper.getAllBloodPressureByDate(patientId, startDate, endDate, isAsc ? "asc" : "desc");
        return transformPoList(list);
    }

    @Override
    public BloodPressureChartBean getBloodPressureRecordsForChartsInApp(int patientId, String startDate, String endDate) {
        Map<String, String> map = baseMapper.getBloodPressureRecordsForChartsInApp(patientId, startDate, endDate);
        BloodPressureChartBean chart = null;
        if (map != null) {
            chart = new BloodPressureChartBean();
            chart.setValues(map.get("values1"));
            chart.setValues2(map.get("values2"));
            chart.setDates(map.get("date"));
        }
        return chart;
    }

    @Override
    public List<BloodBean> getAllBloodPressureBetweenTimeByPage(int patientId, String startDate, String endDate, int page, int pageSize) {
        int pageStart = (page - 1) * pageSize;
        List<DtrdEntRdSigninBloodPressure> list = baseMapper.getAllBloodPressureBetweenTimeByPage(patientId, startDate, endDate, pageStart, pageSize);
        return transformPoList(list);
    }

    private List<BloodBean> transformPoList(List<DtrdEntRdSigninBloodPressure> list) {
        if (CheckUtil.isEmpty(list)) {
            return Collections.EMPTY_LIST;
        }
        List<BloodBean> dataList = new ArrayList<>(list.size());
        list.stream().map(po -> new BloodBean().parseFromPo(po)).forEach(dataList::add);
        return dataList;
    }

    @Override
    public Integer getBloodPressureRecordDateCount(int patientId, String startDate, String endDate) {
        return baseMapper.getBloodPressureRecordDateCount(patientId, startDate, endDate);
    }

    /**
     * 获取血压统计信息
     *
     * @param patientId
     * @param type
     * @param startDate
     * @param endDate
     * @param period
     * @return
     */
    @Override
    public Map getBloodPressureStatistics(int patientId, int type, String startDate, String endDate, int period) {
        Map map = new HashMap(16);
        try {
            String[] timePeriod = handleTimePeriod(startDate, endDate, patientId, type);
            if (timePeriod != null) {
                startDate = timePeriod[0];
                endDate = timePeriod[1];
            }
            // 先检测时间段内是否有数据 ，避免返回全0的记录
            Integer count = getBloodPressureRecordDateCount(patientId, startDate, endDate);
            if (StringUtil.StrTrimInt(count) == 0) {
                // 无记录 不作处理
                log.info("getBloodPressureStatistics." + patientId + " no record between " + startDate + " and " + endDate);
            } else {
                BloodPressureStatisticsBean statisticsBean = new BloodPressureStatisticsBean();
                if (patientId > 0) {
                    statisticsBean = getBloodPressureStatisticsBean(patientId, startDate, endDate);
                    statisticsBean.setCount(statisticsBean.getLowCounts() + statisticsBean.getHighCounts() + statisticsBean.getNormalCounts());
                    statisticsBean.setLowPercent(MathUtil.calculatePercentage(statisticsBean.getLowCounts(), statisticsBean.getCount()));
                    statisticsBean.setHighPercent(MathUtil.calculatePercentage(statisticsBean.getHighCounts(), statisticsBean.getCount()));
                    statisticsBean.setNormalPercent(100 - statisticsBean.getLowPercent() - statisticsBean.getHighPercent());
                    statisticsBean.setPatientId(patientId);
                }
                map.put("statistics", statisticsBean);
            }
        } catch (Exception e) {
            log.error("血压统计异常", e);
        }
        return map;
    }

    /**
     * 统计血压
     *
     * @param patientId
     * @param startDate
     * @param endDate
     * @return
     */
    private BloodPressureStatisticsBean getBloodPressureStatisticsBean(int patientId, String startDate, String endDate) {
        Integer age = getPatientGenderByID(patientId);
        boolean isOldThan65 = age > 65;
        if (CheckUtil.isEmpty(startDate)) {
            startDate = "1970-01-01";
        }
        if (CheckUtil.isEmpty(endDate)) {
            endDate = DateUtils.formatDate();
        }
        float HighTop = 130f;
        float HighTop65 = 150f;
        float HighDown = 90f;
        float LowTop = 80f;
        float LowTop65 = 90f;
        float LowDown = 60f;
        float high = isOldThan65 ? HighTop65 : HighTop;
        float low = isOldThan65 ? LowTop65 : LowTop;
        return baseMapper.getBloodPressureStatisticsBean(patientId, startDate, endDate, high, HighDown, low, LowDown);
    }

    private Integer getPatientGenderByID(int patientId) {
        PatientInfo patientInfo = patientService.selectByPatientId(patientId);
        if (null != patientInfo) {
            return patientInfo.getPatientsSex();
        }
        return 0;
    }

    @Transactional
    @Override
    public boolean saveBloodPressureRecord(BloodPressureDailyInfo info) {
        // 保存晨起血压
        boolean result = saveBloodPressureRecord(info.getPatientId(), BloodPressureRecordType.MORNING.getType(), info.getRecordTime(), info.getMorningHigh(), info.getMorningLow(), info.getMorningHeartRate());
        if (!result) {
            return false;
        }
        // 保存晚血压
        result = saveBloodPressureRecord(info.getPatientId(), BloodPressureRecordType.EVENING.getType(), info.getRecordTime(), info.getNightHigh(), info.getNightLow(), info.getNightHeartRate());

//        if (info.getMorningHeartRate() != null || info.getMorningHigh() != null || info.getMorningLow() != null) {
//            DtrdEntRdSigninBloodPressure morningBloodPressureRecord = new DtrdEntRdSigninBloodPressure().init();
//            morningBloodPressureRecord = info.toMorningRecordPo(morningBloodPressureRecord);
//            LambdaUpdateWrapper<DtrdEntRdSigninBloodPressure> wrapper = getUpdateWrapper(info.getPatientId(),
//                    Constant.BloodPressureRecordType.MORNING.getType(),
//                    date);
//            boolean resultMorning = saveOrUpdate(morningBloodPressureRecord, wrapper);
//            if (!resultMorning) {
//                return false;
//            }
//            DtrdEntRdSigninBloodPressure po = getOne(wrapper);
//            // 补充早晨血压打卡记录
//            recordService.saveSigninRecord(po.getDataId(), patientId,
//                    Constant.RecordType.BLOOD_PRESSURE.getType(),
//                    Constant.BloodPressureRecordType.MORNING.getType(), recordDateStr);
//        }
//        // 晚上血压
//        if (info.getNightHeartRate() != null || info.getNightHigh() != null || info.getNightLow() != null) {
//            DtrdEntRdSigninBloodPressure nightBloodPressureRecord = new DtrdEntRdSigninBloodPressure().init();
//            nightBloodPressureRecord = info.toNightRecordPo(nightBloodPressureRecord);
//            LambdaUpdateWrapper<DtrdEntRdSigninBloodPressure> wrapper = getUpdateWrapper(info.getPatientId(),
//                    Constant.BloodPressureRecordType.EVENING.getType(),
//                    date);
//            boolean resultEvening = saveOrUpdate(nightBloodPressureRecord, wrapper);
//            if (!resultEvening) {
//                return false;
//            }
//            DtrdEntRdSigninBloodPressure po = getOne(wrapper);
//            // 补充晚上血压打卡记录
//            recordService.saveSigninRecord(po.getDataId(), patientId,
//                    Constant.RecordType.BLOOD_PRESSURE.getType(),
//                    Constant.BloodPressureRecordType.EVENING.getType(), recordDateStr);
//        }
        return result;
    }

    private boolean saveBloodPressureRecord(Integer patientId, int type, String recordTime, Integer high, Integer low, Integer heartRate) {
        if (high == null && low == null && heartRate == null) {
            return false;
        }
        DtrdEntRdSigninBloodPressure po = new DtrdEntRdSigninBloodPressure().init();
        po.setPatientId(patientId);
        po.setBloodPressureType(type);
        Date date = null;
        try {
            po.setRecordTime(DateUtils.parseDate(recordTime, DateUtils.datetimeFormat.get().toPattern()));
            date = DateUtils.parseDate(recordTime, DateUtils.date_sdf.get().toPattern());
            po.setRecordDate(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        po.setBloodPressureHighPressure(high);
        po.setBloodPressureLowVoltage(low);
        po.setBloodPressureHeartRate(heartRate);

        LambdaUpdateWrapper<DtrdEntRdSigninBloodPressure> wrapper = getUpdateWrapper(patientId, type, date);
        boolean result = saveOrUpdate(po, wrapper);
        if (!result) {
            return false;
        }
        log.info("BloodPressureRecordServiceImpl.saveBloodPressureRecord.[patientId, type, recordTime, high, low, heartRate] after save dataId=" + po.getDataId());
        // 处理打卡记录
        po = getOne(wrapper);
        String recordDate = DateUtils.dateformat(recordTime, DateUtils.date_sdf.get().toPattern());
        CheckinRecord record = new CheckinRecord();
        record.setRecordType(RecordType.BLOOD_PRESSURE.getType());
        record.setPatientId(patientId);
        record.setRecordTime(recordTime);
        record.setRecordValue(Optional.ofNullable(high).map(String::valueOf).orElse(null));
        record.setRecordValue2(Optional.ofNullable(low).map(String::valueOf).orElse(null));
        record.setRecordValue3(Optional.ofNullable(heartRate).map(String::valueOf).orElse(null));
        log.info("BloodPressureRecordServiceImpl.saveBloodPressureRecord.[patientId, type, recordTime, high, low, heartRate] record=" + record);
        recordService.saveSigninRecord(po.getDataId(), record, type, recordDate);
        return result;
    }

    @Override
    public BloodPressureDailyInfo getBloodPressure(Integer patientId, Date date) {
        LambdaQueryWrapper<DtrdEntRdSigninBloodPressure> queryWrapper = Wrappers.lambdaQuery(DtrdEntRdSigninBloodPressure.class)
                .eq(DtrdEntRdSigninBloodPressure::getIsDel, 0)
                .eq(DtrdEntRdSigninBloodPressure::getPatientId, patientId)
                .eq(DtrdEntRdSigninBloodPressure::getRecordDate, date);
        List<DtrdEntRdSigninBloodPressure> poList = list(queryWrapper);
        BloodPressureDailyInfo bloodPressureDailyInfo = new BloodPressureDailyInfo();
        poList.forEach(po -> {
            if (po.getBloodPressureType().equals(Constant.BloodPressureRecordType.MORNING.getType())) {
                bloodPressureDailyInfo.setMorningHigh(po.getBloodPressureHighPressure());
                bloodPressureDailyInfo.setMorningLow(po.getBloodPressureLowVoltage());
                bloodPressureDailyInfo.setMorningHeartRate(po.getBloodPressureHeartRate());
            }
            if (po.getBloodPressureType().equals(Constant.BloodPressureRecordType.EVENING.getType())) {
                bloodPressureDailyInfo.setNightHigh(po.getBloodPressureHighPressure());
                bloodPressureDailyInfo.setNightLow(po.getBloodPressureLowVoltage());
                bloodPressureDailyInfo.setNightHeartRate(po.getBloodPressureHeartRate());
            }
        });
        return bloodPressureDailyInfo;
    }

    @Override
    public Integer getAvgBloodHighPressure(Integer patientId, String startOfWeek, String endOfWeek) {
        return baseMapper.getAvgBloodHighPressure(patientId, startOfWeek, endOfWeek);
    }

    @Override
    public Integer getAvgBloodLowPressure(Integer patientId, String startOfWeek, String endOfWeek) {
        return baseMapper.getAvgBloodLowPressure(patientId, startOfWeek, endOfWeek);
    }

    @Override
    public boolean saveBloodPressureRecord(BloodBean bloodInfo) {
        Integer type = bloodInfo.getRecordType();
        DtrdEntRdSigninBloodPressure bloodPressure = new DtrdEntRdSigninBloodPressure(bloodInfo);
        LambdaUpdateWrapper<DtrdEntRdSigninBloodPressure> wrapper = Wrappers.lambdaUpdate(DtrdEntRdSigninBloodPressure.class)
                .eq(DtrdEntRdSigninBloodPressure::getPatientId, bloodPressure.getPatientId())
                .eq(DtrdEntRdSigninBloodPressure::getRecordDate, bloodPressure.getRecordDate())
                .eq(DtrdEntRdSigninBloodPressure::getBloodPressureType, bloodPressure.getBloodPressureType());
        boolean result = saveOrUpdate(bloodPressure, wrapper);
        if (!result) {
            return false;
        }
        log.info("BloodPressureRecordServiceImpl.saveBloodPressureRecord.[bloodInfo] after save getDataId()=" + bloodPressure.getDataId());
        // 处理打卡记录
        String recordDate = DateUtils.dateformat(bloodInfo.getRecordTime(), DateUtils.date_sdf.get().toPattern());
        CheckinRecord record = new CheckinRecord();
        record.setRecordType(RecordType.BLOOD_PRESSURE.getType());
        record.setPatientId(bloodInfo.getPatientId());
        record.setRecordTime(bloodInfo.getRecordTime());
        record.setRecordValue(Optional.ofNullable(bloodInfo.getHigh()).map(String::valueOf).orElse(null));
        record.setRecordValue2(Optional.ofNullable(bloodInfo.getLow()).map(String::valueOf).orElse(null));
        record.setRecordValue3(Optional.ofNullable(bloodInfo.getHeart()).map(String::valueOf).orElse(null));
        log.info("BloodPressureRecordServiceImpl.saveBloodPressureRecord.[bloodInfo] record=" + record);
        recordService.saveSigninRecord(bloodPressure.getDataId(), record, type, recordDate);
        return true;
    }

    @Override
    public Integer getAvgBloodHeartrate(Integer patientId, String startOfWeek, String endOfWeek) {
        return baseMapper.getAvgBloodHeartrate(patientId, startOfWeek, endOfWeek);
    }

    @Override
    public DtrdEntRdSigninBloodPressure getBloodPressureRecordLatestByPatientId(Integer patientId) {
        LambdaQueryWrapper<DtrdEntRdSigninBloodPressure> wrapper = Wrappers.lambdaQuery(DtrdEntRdSigninBloodPressure.class)
                .eq(DtrdEntRdSigninBloodPressure::getPatientId, patientId)
                .orderByDesc(DtrdEntRdSigninBloodPressure::getCreateTime)
                .last("limit 1");
        return getOne(wrapper);
    }

    @Override
    public Float getFirstBloodPressureValue(Integer patientId, Constant.BloodPressure bloodPressure) {
        return baseMapper.getFirstBloodPressureValue(patientId, bloodPressure);
    }

    /**
     * 处理请求时间
     *
     * @param
     * @param type 0、2直接获取参数值，3 以当天为结束日期按照指定周期倒推开始时间
     * @return
     */
    private String[] handleTimePeriod(String startDate, String endDate, int period, int type) {
        String[] timePeriod = null;
        if (type == 0 || type == 2) {
            if (endDate.equals("")) {
                endDate = startDate;
            }
            timePeriod = new String[2];
            timePeriod[0] = startDate;
            timePeriod[1] = endDate;
        } else if (type == 3) {
            // 按照周期查询 0：全部 1：一周、2：两周、3：一月、4：两月 5 三月 6 半年 7 一年
            endDate = DateUtils.formatDate();
            int dateSub = 0;
            switch (period) {
                case 0:
                    dateSub = Integer.MIN_VALUE;
                    break;
                case 1:
                    dateSub = -6;
                    break;
                case 2:
                    dateSub = -13;
                    break;
                case 3:
                    dateSub = -29;
                    break;
                case 4:
                    dateSub = -59;
                    break;
                case 5:
                    dateSub = -89;
                    break;
                case 6:
                    dateSub = -179;
                    break;
                case 7:
                    dateSub = -364;
                    break;
                default:
                    dateSub = Integer.MIN_VALUE;
                    break;
            }
            if (dateSub == Integer.MIN_VALUE) {
                startDate = "1970-01-01";
            } else {
                startDate = DateUtils.dateAdd(dateSub);
            }
            timePeriod = new String[2];
            timePeriod[0] = startDate;
            timePeriod[1] = endDate;
        }
        return timePeriod;
    }

    private Integer parseToInt(String object) {
        int result = 0;
        if (object == null) {
            return 0;
        }
        String number = String.valueOf(object).trim();
        if (number.equals("") || number.equals("null")) {
            return 0;
        }
        try {
            result = new Double(number).intValue();
            return result;
        } catch (Exception ex) {
            // ex.printStackTrace();
        }
        return 0;
    }

    private Float parseFromString(String number) {
        float result = 0f;
        if (number == null || "".equals(number) || number.equals("null")) {
            return 0.00f;
        }
        try {
            result = Float.parseFloat(number);
            return result;
        } catch (Exception ex) {
            // ex.printStackTrace();
        }
        return result;
    }

    private LambdaUpdateWrapper<DtrdEntRdSigninBloodPressure> getUpdateWrapper(int patientId, int bloodType, Date recordDate) {
        return Wrappers.lambdaUpdate(DtrdEntRdSigninBloodPressure.class)
                .eq(DtrdEntRdSigninBloodPressure::getPatientId, patientId)
                .eq(DtrdEntRdSigninBloodPressure::getBloodPressureType, bloodType)
                .eq(DtrdEntRdSigninBloodPressure::getIsDel, 0)
                .eq(DtrdEntRdSigninBloodPressure::getRecordDate, recordDate);
    }

}
